package com.wy.dao;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;

import org.nutz.dao.Cnd;
import org.nutz.dao.Dao;
import org.nutz.dao.Sqls;
import org.nutz.dao.entity.Entity;
import org.nutz.dao.entity.annotation.Table;
import org.nutz.dao.pager.Pager;
import org.nutz.dao.sql.Sql;
import org.nutz.dao.util.cri.SimpleCriteria;
import org.nutz.lang.Strings;
import org.springframework.beans.factory.annotation.Autowired;

import com.wy.model.AbsModel;
import com.wy.page.AbsPage;
import com.wy.result.Result;
import com.wy.result.Result.ResultBuilder;
import com.wy.result.ResultException;
import com.wy.utils.ClassUtils;
import com.wy.utils.StrUtils;

/**
 * 公用dao层
 * @description 封装nutz的基础dao层
 * @author paradiseWy
 * @date 2019年4月11日 下午4:26:28
 * @git {@link https://github.com/mygodness100}
 */
@SuppressWarnings("unchecked")
public abstract class AbsDao<T> {
	@Autowired
	protected Dao dao;

	public Class<T> clazz;

	public AbsDao() {
		ParameterizedType parameterizedType = (ParameterizedType) getClass().getGenericSuperclass();
		Type type = parameterizedType.getActualTypeArguments()[0];
		clazz = (Class<T>) type;
	}

	public Dao getDao() {
		return dao;
	}

	public Class<T> getClassOfT() {
		return clazz;
	}

	public Entity<T> getEntry() {
		return dao.getEntity(clazz);
	}

	public T save(T model) {
		return dao.insert(model);
	}

	/**
	 * 数据添加排序,实体类中必须有sort字段,数据库中必须有sort
	 * @param t 实体类
	 * @return 添加完成后的数据
	 */
	public T saveSort(T t) {
		return saveSort(t, "sort");
	}

	/**
	 * 自定义数据添加排序,实体类中的字段必须是驼峰,数据库中是下划线,或都是小写
	 * @param t 实体类数据
	 * @param sortColumn 自定义排序字段,必须是驼峰格式或全小写
	 * @return 添加后的数据
	 */
	public T saveSort(T t, String sortColumn) {
		try {
			Field field = clazz.getDeclaredField(sortColumn);
			field.setAccessible(true);
			// 若排序字段未传值,则进行数据库查询
			if (Objects.isNull(field.get(t)) || Integer.parseInt(field.get(t).toString()) <= 0) {
				int sort = getMaxSort(sortColumn, getEntry().getTableName());
				field.set(t, sort);
			}
			return save(t);
		} catch (NoSuchFieldException | SecurityException | IllegalArgumentException
				| IllegalAccessException e) {
			e.printStackTrace();
			throw new ResultException("添加排序失败");
		}
	}

	/**
	 * 批量新增,需要主键自增,若主键非自增,需要先设置
	 * @param entitys 实体类数据
	 * @return 新增后的数据
	 */
	public List<T> saves(List<T> entitys) {
		return dao.fastInsert(entitys);
	}

	public int delete(Serializable id) {
		return delete(id, clazz);
	}

	public int delete(Serializable id, Class<?> clazz) {
		if (ClassUtils.isIntegral(clazz)) {
			return dao.delete(clazz, Long.parseLong(id.toString()));
		} else if (String.class == id.getClass()) {
			return dao.delete(clazz, id.toString());
		} else {
			return dao.delete(id);
		}
	}

	public int deletes(List<? extends Serializable> ids) {
		return deletes(ids, clazz);
	}

	public int deletes(List<? extends Serializable> ids, Class<?> clazz) {
		int i = 0;
		for (Serializable id : ids) {
			i += delete(id, clazz);
		}
		return i;
	}

	/**
	 * 全字段更新,若传null,数据库也会更新为null
	 * @param model 需要更新的数据
	 */
	public int update(T model) {
		return dao.update(model);
	}

	/**
	 * 批量全字段更新
	 * @param entitys 需要更新的全字段数据
	 */
	public int updates(List<T> entitys) {
		int i = 0;
		for (T t : entitys) {
			i += dao.update(t);
		}
		return i;
	}

	/**
	 * 执行存储过程
	 * @param procName 存储过程名
	 * @param params 参数
	 */
	public List<Map<String, Object>> execProc(String procName, Object... params) {
		StringBuilder sb = new StringBuilder("exec ").append(procName).append(" ");
		if (params.length == 0) {
			return getMaps(sb.toString());
		}
		Sql sqls = Sqls.create("");
		for (int i = 0, leng = params.length; i < leng; i++) {
			sb.append(" @params").append(i).append(",");
			sqls.setParam("params" + i, params[i]);
		}
		sqls.setSourceSql(sb.toString().substring(0, sb.toString().length() - 1));
		return getMaps(sqls);
	}

	public boolean hasValue(String column, Object value) {
		return hasValue(getEntry().getTableName(), column, value);
	}

	public boolean hasValue(Class<?> clazz, String column, Object value) {
		String tableName = "";
		if (clazz.isAnnotationPresent(Table.class)) {
			tableName = clazz.getAnnotation(Table.class).value();
		}
		if (StrUtils.isBlank(tableName)) {
			throw new ResultException("该实体类没有指定表");
		}
		return hasValue(tableName, column, value);
	}

	public boolean hasValue(String table, String column, Object value) {
		Sql sql = Sqls.fetchInt("select count(*) from $table where $key = @value")
				.setVar("table", table).setVar("key", column).setParam("value", value);
		return dao.execute(sql).getInt() == 1;
	}

	/**
	 * 获得泛型在数据库中的排序字段的当前最大排序值,默认排序字段为sort
	 * @return 最大排序值
	 */
	public int getMaxSort() {
		return getMaxSort(getEntry().getTableName());
	}

	/**
	 * 获得指定数据库表的数字类型字段的当前最大值,默认查询排序字段sort的值
	 * @param tableName 表名
	 * @return 最大排序值
	 */
	public int getMaxSort(String tableName) {
		return getMaxSort("sort", tableName);
	}

	/**
	 * 获得指定数据库指定数字类型字段的最大值
	 * @param maxColumn 需要后的最大值的数据集字段
	 * @param tableName 数据库表名
	 * @return 最大值
	 */
	public int getMaxSort(String maxColumn, String tableName) {
		Sql sql = Sqls.fetchInt("select max($maxColumn) from $table").setVar("maxColumn", maxColumn)
				.setVar("table", tableName);
		return dao.execute(sql).getInt(-1) + 1;
	}

	public String getStr(String sqlStr) {
		return dao.execute(Sqls.fetchString(sqlStr)).getString();
	}

	public String getStr(Sql sql) {
		return dao.execute(sql.setCallback(Sqls.callback.str())).getString();
	}

	public List<String> getStrs(Sql sql) {
		return dao.execute(sql.setCallback(Sqls.callback.strs())).getList(String.class);
	}

	public int getInt(String sqlStr) {
		return getInt(Sqls.create(sqlStr));
	}

	public int getInt(Sql sql) {
		return dao.execute(sql.setCallback(Sqls.callback.integer())).getInt(0);
	}

	public T getById(Serializable id) {
		return getById(id, clazz);
	}

	public <K> K getById(Serializable id, Class<K> clazz) {
		if (ClassUtils.isIntegral(clazz)) {
			return dao.fetch(clazz, Long.parseLong(id.toString()));
		} else if (String.class == id.getClass()) {
			return dao.fetch(clazz, id.toString());
		} else {
			return dao.fetch((K) id);
		}
	}

	/**
	 * 存储过程获取树形结构组织机构的所有子级id
	 * @param id 父级id
	 * @return 父级,包括所有子级id
	 */
	public String[] getOrgChild(String id) {
		Sql sql = Sqls.queryString("exec HBYXJC..P_getOrgChildrenId @id").setParam("id", id);
		return (String[]) dao.execute(sql).getResult();
	}

	/**
	 * 存储过程获取树形结构组织机构的所有子级id
	 * @param id 父级id
	 * @return 父级,包括所有子级id
	 */
	public long[] getOrgChild(long id) {
		Sql sql = Sqls.queryString("exec P_getOrgChildrenId @id").setParam("id", id);
		List<String> datas = dao.execute(sql).getList(String.class);
		return datas.stream().mapToLong(str -> Long.parseLong(str)).toArray();
	}

	public List<Map<String, Object>> getMaps(String sqlStr) {
		return getMaps(Sqls.create(sqlStr));
	}

	public List<Map<String, Object>> getMaps(Sql sql) {
		return (List<Map<String, Object>>) dao.execute(sql.setCallback(Sqls.callback.maps()))
				.getResult();
	}

	public Map<String, Object> getMap(String sqlStr) {
		return getMap(Sqls.create(sqlStr));
	}

	public Map<String, Object> getMap(Sql sql) {
		return (Map<String, Object>) dao.execute(sql.setCallback(Sqls.callback.map())).getResult();
	}

	/**
	 * 根据单个实体类中的非null参数为条件获得该实体类的集合
	 * @param entity 单个实体类参数
	 * @return 实体类参数集合
	 */
	public List<T> getEntitys(T model) {
		SimpleCriteria cri = getCri(model);
		if (model instanceof AbsModel) {
			AbsModel<T> base = (AbsModel<T>) model;
			return dao.query(clazz, cri, new Pager(base.getPageIndex(), base.getPageSize()));
		}
		return dao.query(clazz, cri);
	}

	/**
	 * 单表查询,无参数查询;有分页参数则分页查询,无分页参数则不分页查询
	 * @param entity 单表实体类
	 */
	public Result getEntitys(Integer pageIndex, Integer pageSize) {
		boolean page = false;
		if (pageIndex != null && pageIndex > 0) {
			page = true;
			if (pageSize == null || pageSize <= 0) {
				pageSize = 10;
			}
		}
		return getPageResult(page, pageIndex, pageSize, null);
	}

	/**
	 * 单表查询,有参数,但是只能查询参数相等的条件;有分页参数则分页查询,若无分页参数则不分页查询
	 * @param model 单表实体类
	 */
	public Result getEntitysByCnd(T model) {
		SimpleCriteria cri = getCri(model);
		if (model instanceof AbsModel) {
			AbsModel<T> base = (AbsModel<T>) model;
			return getPageResult(base.hasPager(), base.getPageIndex(), base.getPageSize(), cri);
		}
		return getPageResult(false, 0, 0, cri);
	}

	/**
	 * 根据单个实体类中的非null参数为条件获得表中符合条件的数据总数
	 * @param entity 单个实体类参数
	 * @return 符合条件的数据总数
	 */
	public int getEntitysCount(T entity) {
		SimpleCriteria cri = getCri(entity);
		return dao.count(entity.getClass(), cri);
	}

	private SimpleCriteria getCri(T model) {
		Field[] fields = clazz.getDeclaredFields();
		if (fields.length == 0) {
			throw new ResultException("该实体类中没有字段");
		}
		SimpleCriteria cri = Cnd.cri();
		for (Field field : fields) {
			field.setAccessible(true);
			if (Modifier.isStatic(field.getModifiers()) || Modifier.isFinal(field.getModifiers())) {
				continue;
			}
			try {
				Object value = field.get(model);
				if (Objects.isNull(value)) {
					continue;
				}
				cri.where().andEquals(field.getName(), value);
			} catch (IllegalArgumentException | IllegalAccessException e) {
				e.printStackTrace();
			}
		}
		return cri;
	}

	private Result getPageResult(boolean hasPager, int pageIndex, int pageSize,
			SimpleCriteria cri) {
		ResultBuilder builder = Result.builder().pageIndex(pageIndex).pageSize(pageSize);
		if (hasPager) {
			return builder.total(dao.count(clazz, cri))
					.data(dao.query(clazz, cri, new Pager(pageIndex, pageSize))).build();
		} else {
			List<T> datas = dao.query(clazz, cri);
			return builder.total(datas.size()).data(datas).build();
		}
	}

	/**
	 * 联表分页查询,参数可有可无;有分页参数则查分页,若无分页参数则查全部数据
	 * @param page 过滤参数
	 */
	public Result getList(AbsPage model) {
		int total = 0;
		SimpleCriteria criteria = Cnd.cri();
		model.addCnds(dao, criteria.where());
		if (Strings.isNotBlank(model.addGroupBy())) {
			criteria.groupBy(model.addGroupBy());
		}
		if (model.hasPager()) {
			Sql dataSql = Sqls.create("select $columns from $table $condition")
					.setVar("columns", model.addColumns()).setVar("table", model.addTables())
					.setCondition(criteria);
			Sql countSql = Sqls.fetchInt("select count(*) from ($dataSql) a ")
					.setVar("dataSql", dataSql.toString()).setCondition(criteria);
			criteria.setPager(new Pager(model.getPageIndex(), model.getPageSize()));
			total = dao.execute(countSql).getInt(0);
			if (total <= 0) {
				return Result.page(null, 0, 0, 0);
			}
		}
		model.addOrderBy(criteria.getOrderBy());
		Sql dataSql = Sqls.create("select $columns from $table $condition")
				.setVar("columns",
						StrUtils.isNotBlank(model.addColumns()) ? handleColumns(model.addColumns())
								: model.addSpecialColumns())
				.setVar("table", model.addTables()).setCondition(criteria);
		if (criteria.getPager() != null) {
			dataSql.setPager(criteria.getPager());
		} else {
			return Result.ok(getMaps(dataSql));
		}
		return Result.page(getMaps(dataSql), model.getPageIndex(), model.getPageSize(), total);
	}

	/**
	 * 对驼峰字段进行处理,变成下划线 驼峰别名
	 * @param columns 字段集合
	 * @return 字段字符串
	 */
	private String handleColumns(String columns) {
		String[] src = columns.split(",");
		List<String> des = new ArrayList<>();
		for (String column : src) {
			des.add(MessageFormat.format("{0} {1}", StrUtils.hump2Snake(column),
					column.contains(".") ? column.substring(column.indexOf(".") + 1) : column));
		}
		return String.join(",", des);
	}
}